﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Alvin.Common.Utils
{
    public static class Md5Util
    {
        /// <summary>
        /// 读取文件，并计算文件流的Md5值
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        /// <example></example>
        public static string Md5Stream(string filePath)
        {
            string md5;
            using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                md5 = Md5Stream(fs);
            }
            return md5;
        }

        /// <summary>
        /// 读取文件，并计算文件块的Md5值
        /// </summary>
        /// <param name="filePath"></param>
        public static String Md5File(string filePath)
        {
            var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            var bufferSize = 1048576; // 缓冲区大小，1MB
            var buff = new byte[bufferSize];

            var md5 = new MD5CryptoServiceProvider();
            md5.Initialize();

            long offset = 0;
            while (offset < fs.Length)
            {
                long readSize = bufferSize;
                if (offset + readSize > fs.Length)
                {
                    readSize = fs.Length - offset;
                }

                fs.Read(buff, 0, Convert.ToInt32(readSize)); // 读取一段数据到缓冲区

                if (offset + readSize < fs.Length) // 不是最后一块
                {
                    md5.TransformBlock(buff, 0, Convert.ToInt32(readSize), buff, 0);
                }
                else // 最后一块
                {
                    md5.TransformFinalBlock(buff, 0, Convert.ToInt32(readSize));
                }

                offset += bufferSize;
            }

            fs.Close();
            var result = md5.Hash;
            md5.Clear();

            var sb = new StringBuilder(32);
            for (var i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }
            return sb.ToString();
        }

        /// <summary>
        /// 计算输入流的Md5值
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static String Md5Stream(Stream stream)
        {
            var md5 = new MD5CryptoServiceProvider();
            md5.ComputeHash(stream);

            var b = md5.Hash;
            md5.Clear();

            var sb = new StringBuilder(32);
            for (var i = 0; i < b.Length; i++)
            {
                sb.Append(b[i].ToString("X2"));
            }
            return sb.ToString();
        }

        /// <summary>
        /// Hash an input string and return the hash as a 32 character hexadecimal string.
        /// </summary>
        /// <param name="input">input str</param>
        /// <returns></returns>
        public static string Md5String(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            var md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            var data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            var sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (var i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }
        
        /// <summary>
        /// Verify a hash against a string.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="hash"></param>
        /// <returns></returns>
        public static bool VerifyMd5(string input, string hash)
        {
            // Hash the input.
            var hashOfInput = Md5String(input);

            // Create a StringComparer an comare the hashes.
            var comparer = StringComparer.OrdinalIgnoreCase;

            return 0 == comparer.Compare(hashOfInput, hash);
        }
    }
}
